package com.lb.bs.client.adapter;

import com.lb.bs.client.adapter.proxy.LBProxyFactory;
import com.lb.bs.client.callback.UpdateCallBackService;
import com.lb.bs.client.config.StaticConfig;
import com.lb.bs.client.enums.StateEnum;
import com.lb.bs.client.factory.SingletonFactory;
import com.lb.bs.client.manager.CoreMgr;
import com.lb.bs.client.manager.ScanMgr;
import com.lb.bs.client.repository.BeanHolder;
import com.lb.bs.client.repository.ScanStaticModel;
import com.lb.bs.client.util.CollectionUtils;
import com.lb.bs.client.util.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * program: LB-BS-Client
 * author: bsworld.xie
 * create: 2020-05-18 14:51
 * description:
 */
public abstract class AbstractStarter {

    private final Logger logger = LoggerFactory.getLogger(AbstractStarter.class);

    private static volatile AtomicReference initFlag = new AtomicReference(StateEnum.UNSTART);

    protected StaticConfig staticConfig;

    protected ScanMgr scanMgr;


    protected ScanStaticModel scanStaticModel;

    private CoreMgr coreMgr;


    public AbstractStarter appendStaticConfig(StaticConfig staticConfig) {
        if (staticConfig == null) {
            throw new IllegalStateException("append staticConfig is null");
        }
        String nameSpace = staticConfig.getNameSpace();
        if (StringUtils.isNotEmpty(nameSpace) && nameSpace.startsWith("/")) {
            throw new IllegalStateException("lb-bs-client nameSpace can not start with /");
        }
        String zkHost = staticConfig.getZkHost();
        if (StringUtils.isEmpty(zkHost)) {
            throw new IllegalStateException("lb-bs-client staticConfig, zookeeper host is empty");
        }
        this.staticConfig = staticConfig;
        return this;
    }

    private void init() {
        Class<? extends AbstractStarter> clazz = this.getClass();
        BeanHolder holder = BeanHolder.getHolder();
        holder.registerBean(clazz, this);
        logger.info("lb-bs-client, start init");
        holder.registerBean(staticConfig.getClass(), staticConfig);
        scan();
        registerBean();
    }

    public void start() {
        try {
            if (initFlag.compareAndSet(StateEnum.UNSTART, StateEnum.STARTING)) {
                init();
                before();
                startCore();
                after();
                initFlag.set(StateEnum.STARTED);
                logger.info("LB-BS-CLIENT start success");
            }
        } catch (Throwable t) {
            initFlag.set(StateEnum.CLOSE);
            logger.error("start（）, error:", t);
            throw t;
        }
    }

    protected void before() {

    }

    protected void after() {

    }


    protected void destroyCore() {
        try {
            boolean compareSuccess = initFlag.compareAndSet(StateEnum.STARTED, StateEnum.CLOSE);
            if (compareSuccess) {
                this.coreMgr.close();
            }
        } catch (Throwable t) {
            initFlag.set(StateEnum.CLOSE);
            logger.error("LBSecondBean.class, destroy failed, error:", t);
        }
    }


    private void startCore() {
        CoreMgr holder = CoreMgr.getHolder(scanStaticModel, staticConfig);
        holder.runStaticEvent();
        this.coreMgr = holder;
    }


    private void scan() {
        StaticConfig staticConfig = this.staticConfig;
        ScanMgr scanMgr = SingletonFactory.getInstance(ScanMgr.class);
        List<String> scanPackList = StringUtils.isNotEmpty(staticConfig.getScanPackages()) ? Stream.of(staticConfig.getScanPackages().trim().split(",")).collect(Collectors.toList()) : new ArrayList<>();
        if (CollectionUtils.isEmpty(scanPackList)) {
            throw new IllegalStateException("scan package isEmpty");
        }
        ScanStaticModel scanStaticModel = scanMgr.scanPackage(scanPackList);
        if (scanStaticModel == null) {
            throw new IllegalStateException("scan model  is null");
        }
        this.scanStaticModel = scanStaticModel;
        this.scanMgr = scanMgr;
    }


    private void registerBean() {
        ScanStaticModel scanStaticModel = this.scanStaticModel;
        Set<Class<?>> clazzList = scanStaticModel.getClazzList();
        BeanHolder holder = BeanHolder.getHolder();
        for (Class<?> clazz : clazzList) {
            try {
                Object originBean = clazz.newInstance();
                holder.registerBean(clazz, originBean);
                Object proxy = LBProxyFactory.getProxy(clazz, originBean);
                holder.registerProxy(clazz, proxy);
                holder.registerLbClassBean(originBean);
            } catch (Exception e) {
                logger.error("lb-bs-client bean register error:", e);
                throw new IllegalStateException("lb-bs-client bean register error", e);
            }
        }
        Set<Class<? extends UpdateCallBackService>> updateClazzList = scanStaticModel.getUpdateClazzList();
        for (Class<? extends UpdateCallBackService> updateClazz : updateClazzList) {
            try {
                UpdateCallBackService originBean = (UpdateCallBackService) holder.getBean(updateClazz);
                if (originBean == null) {
                    originBean = updateClazz.newInstance();
                }
                holder.registerBean(updateClazz, originBean);
                Object proxy = LBProxyFactory.getProxy(updateClazz, originBean);
                holder.registerProxy(updateClazz, proxy);
                holder.registerLbUpdateBean(originBean);
            } catch (Exception e) {
                logger.error("lb-bs-client bean register error:", e);
                throw new IllegalStateException("lb-bs-client bean register error", e);
            }
        }
    }
}
